<?php

use App\Library\MySeasLog;
use App\Library\Requests\Base\BaseDTORequest;
use App\Library\Requests\Base\BasePageRequest;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;

/**
 * fail
 * @param int $code
 * @return JsonResponse
 */
function fail(int $code = -1): JsonResponse
{
    $arr = [
        'code' => $code,
        'message' => __('errorCode.' . $code),
        'timestamp' => now()->format('Y-m-d H:i:s.u'),
    ];
    MySeasLog::warning($arr);
    return response()->json($arr);
}

/**
 * success
 * @param mixed $data
 * @param string $msg
 * @return JsonResponse
 */
function success(mixed $data = [], string $msg = 'success'): JsonResponse
{
    $jsonData = [
        'code' => 200,
        'msg' => $msg,
        'data' => $data,
        'timestamp' => now()->format('Y-m-d H:i:s.u'),
    ];
    MySeasLog::notice($jsonData);
    return response()->json($jsonData);
}

/**
 * failCodeMessage
 * @param int $code
 * @param string $message
 * @return JsonResponse
 */
function failCodeMessage(int $code = -1, string $message = ''): JsonResponse
{
    $jsonData = [
        "code" => $code,
        "message" => $message,
        'timestamp' => now()->format('Y-m-d H:i:s.u'),
    ];
    MySeasLog::warning($jsonData);
    return response()->json($jsonData);
}

/**
 * failMessage
 * @param string $message
 * @return JsonResponse
 */
function failMessage(string $message = ''): JsonResponse
{
    return failCodeMessage(-1, $message);
}

/**
 * 将驼峰式命名转换为下划线式命名
 * @param string|array $obj
 * @param array $arr
 * @return string|array
 */
function humpToLine(string|array $obj, array $arr = []): string|array
{
    if (is_string($obj)) return strtolower(preg_replace('/[A-Z]/', '_$0', lcfirst($obj)));
    else if (array_key_first($obj) === 0) {
        foreach ($obj as $k => $v) {
            if ($v instanceof Expression) continue;
            else if (is_array($v)) $obj[$k] = humpToLine($v);
            else $obj[$k] = strtolower(preg_replace('/[A-Z]/', '_$0', lcfirst($v)));
        }
        return $obj;
    } else {
        foreach ($obj as $k => $v) $arr[strtolower(preg_replace('/[A-Z]/', '_$0', lcfirst($k)))] = $v;
        return $arr;
    }
}

/**
 * 将下划线式命名转换为驼峰式命名
 * @param string|array $obj
 * @param array $arr
 * @return array|string
 */
function lineToHump(string|array $obj, array $arr = []): array|string
{
    if (is_string($obj)) return lcfirst(str_replace('_', '', ucwords($obj, '_')));
    else if (array_key_first($obj) === 0) {
        foreach ($obj as $v) $arr[] = lcfirst(str_replace('_', '', ucwords($v, '_')));
    } else {
        foreach ($obj as $k => $v) $arr[lcfirst(str_replace('_', '', ucwords($k, '_')))] = $v;
    }
    return $arr;
}

/**
 * random
 * @param int $number
 * @param int $num
 * @param string $str
 * @return string
 */
function random(int $number = 6, int $num = 0, string $str = ''): string
{
    $string = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    for ($i = 0; $i < $number; $i++) {
        $str .= $string[mt_rand(0, $num ? 9 : strlen($string) - 1)];
    }
    return $str;
}

/**
 * serialNumber
 * @param int $number
 * @return string
 */
function serialNumber(int $number = 20): string
{
    if ($number === 20) return now()->format('YmdHisu');
    else if ($number > 20) return now()->format('YmdHisu') . random($number - 20, 1);
    else return random($number, 1);
}


/**
 * encodeId
 * @param int $id
 * @return int
 */
function encodeId(int $id): int
{
    $sid = ($id & 0xff000000);
    $sid += ($id & 0x0000ff00) << 8;
    $sid += ($id & 0x00ff0000) >> 8;
    $sid += ($id & 0x0000000f) << 4;
    $sid += ($id & 0x000000f0) >> 4;
    $sid ^= 2147483647;
    return $sid;
}

/**
 * decodeId
 * @param int $sid
 * @return int
 */
function decodeId(int $sid): int
{
    $sid ^= 2147483647;
    $id = ($sid & 0xff000000);
    $id += ($sid & 0x00ff0000) >> 8;
    $id += ($sid & 0x0000ff00) << 8;
    $id += ($sid & 0x000000f0) >> 4;
    $id += ($sid & 0x0000000f) << 4;
    return $id;
}

/**
 * paginate
 * @param LengthAwarePaginator|BasePageRequest $paginate
 * @return array
 */
function paginate(LengthAwarePaginator|BasePageRequest $paginate): array
{
    if ($paginate instanceof BasePageRequest) {
        $array = [
            'limit' => is_null($paginate->limit) ? 15 : $paginate->limit,
            'page' => is_null($paginate->page) ? 1 : $paginate->page,
            'total' => 0,
            'totalPage' => 0,
            'data' => [],
        ];
    } else {
        $array = [
            'limit' => $paginate->perPage(),
            'page' => $paginate->currentPage(),
            'total' => $paginate->total(),
            'totalPage' => $paginate->lastPage(),
            'data' => $paginate->items(),
        ];
    }
    unset($paginate);
    return $array;
}

/**
 * helpBuilder
 * @param Builder $builder
 * @param BaseDTORequest $request
 * @return Builder
 */
function helpBuilder(Builder $builder, BaseDTORequest $request): Builder
{
    $columns = handleRequest($request);
    foreach ($columns as $key => $value) {
        if (in_array($key, ['limit', 'page', 'ascDesc', 'isLogoff'])) continue;
        else if (is_null($value) || is_null($request->$key)) continue;
        else if ($key === 'sortColumn') {
            $value = str_contains($value, '.') ? $value : $builder->from . '.' . $value;
            $builder->orderBy(humpToLine($value), $request->ascDesc ?? 'asc');
        } else if ($key === 'deletedAt') {
            $field = humpToLine($builder->from . '.' . $key);
            $value ? $builder->whereNotNull($field) : $builder->whereNull($field);
        } else if ($key === 'startAt' && strlen($value) === 19) {
            $field = humpToLine($builder->from . '.' . 'createdAt');
            $builder->where($field, '>=', $value);
        } else if ($key === 'endAt' && strlen($value) === 19) {
            $field = humpToLine($builder->from . '.' . 'createdAt');
            $builder->where($field, '<=', $value);
        } else {
            if (is_array($request->rules()[$key])) {
                foreach ($request->rules()[$key] as $v) {
                    if ($v instanceof LikeRule) {
                        if (str_contains($key, '.')) $field = humpToLine($key);
                        else $field = humpToLine($builder->from . '.' . $key);
                        $builder->where($field, 'like', "%$value%");
                        break;
                    }
                }
            } else if (count(explode('.', $key)) === 2) {
                $builder->where(humpToLine($key), $value);
            } else {
                if (str_contains($key, '.')) $field = humpToLine($key);
                else $field = humpToLine($builder->from . '.' . $key);
                $builder->where($field, $value);
            }
        }
    }
    return $builder;
}

/**
 * handleRequest
 * @param BaseDTORequest $request
 * @param array|null $needle
 * @return array
 */
function handleRequest(BaseDTORequest $request, array $needle = null): array
{
    $return = [];
    foreach ($request->rules() as $k => $v) {
        if (is_null($request->$k)) continue;
        else if (!is_null($needle) && !in_array($k, $needle)) continue;
        else $return[$k] = $request->$k;
    }
    return $return;
}
